package org.ckhd.calculate24;

import java.util.*;
import java.util.stream.Collectors;

/**
 * “算24点”游戏
 */
public class Calculate24 {

    /**
     * 输出结果
     *
     * @param numList 任意数量的正整数
     * @param target  计算的目标
     */
    public static void printResult(List<Integer> numList, int target) {
        Date date1 = new Date();
        // 计算24
        List<String> resultList = calculate24(numList, target);
        // 循环输出所有解法
        if (resultList.isEmpty()) {
            System.out.println("无结果");
        } else {
            System.out.println(numList + " 的所有解法：");
            if (resultList.size() > 5) {
                resultList = resultList.subList(0, 5);
                System.out.println("由于解法较多，以为您筛选前5个结果");
            }
            resultList.forEach(s -> System.out.println(s));
        }
        Date date2 = new Date();
        System.out.println("用时：" + (date2.getTime() - date1.getTime()) + " 毫秒");
    }

    /**
     * 计算24
     *
     * @param numList 任意数量的正整数
     * @param target  计算的目标
     */
    public static List<String> calculate24(List<Integer> numList, int target) {
        // 将每个数字转化为分母为1的分数，并嵌套在一个大list中，形成listList
        List<List<MyFraction>> listList = Arrays.asList(numList.stream().map(num -> new MyFraction(num)).collect(Collectors.toList()));
        // 针对listList，循环执行“两数四则穷举”，直到每个list长度为1为止
        while (listList.get(0).size() > 1) listList = calculateReduce(listList);
        // 筛选结果为24的list
        Set<String> resultSet = new LinkedHashSet<>();
        listList.forEach(list -> {
            if (list.get(0).compareTo(new MyFraction(target)) == 0)
                resultSet.add(new StringBuilder().append(list.get(0).getSourceStr()).append("=").append(target).toString());
        });
        return new ArrayList<>(resultSet);
    }

    /**
     * 两数四则穷举：从每个list中任取2个数进行所有四则运算，用结果替代这两个数。这样下来，运算后每个list长度-1
     *
     * @param listList 待计算的listList
     */
    public static List<List<MyFraction>> calculateReduce(List<List<MyFraction>> listList) {
        List<List<MyFraction>> result = new ArrayList<>();
        for (List<MyFraction> list : listList) { // 从每个list中任取2个数进行所有四则运算
            for (int i = 0; i < list.size(); i++) { // 运算符左边的数
                for (int j = 0; j < list.size(); j++) { // 运算符右边的数
                    if (j == i) continue;

                    // 需要进行四则运算的两个数
                    MyFraction f1 = list.get(i), f2 = list.get(j);
                    // 临时list，分别存储加、减、乘、除后的新的数
                    List<List<MyFraction>> tempList = new ArrayList<>(4);

                    //加
                    if (!(
                            //f1若没有来源或来源为乘除，则f1顺序不能比f2大
                            ((f1.source == null || f1.source.sign.equals(Constants.SIGN_MULTIPLY) || f1.source.sign.equals(Constants.SIGN_DIVIDE)) && f1.compareToByIndex(f2) > 0)
                                    //f1的来源若为加，则后者的顺序不能比f2大
                                    || (f1.source != null && f1.source.sign.equals(Constants.SIGN_ADD) && f1.source.f2.compareToByIndex(f2) > 0)
                                    //f1的来源不能为减
                                    || (f1.source != null && f1.source.sign.equals(Constants.SIGN_SUBTRACT))
                                    //f2的来源不能为加减
                                    || (f2.source != null && (f2.source.sign.equals(Constants.SIGN_ADD) || f2.source.sign.equals(Constants.SIGN_SUBTRACT)))
                    )) tempList.add(new ArrayList<>(Arrays.asList(f1.add(f2))));
                    //减
                    if (!(
                            // f1的顺序不能比f2小
                            (f1.compareToByIndex(f2) < 0)
                                    // f2不能为0
                                    || (f2.compareTo(new MyFraction(0)) == 0)
                                    // f1的来源若为减，则后者的顺序不能比f2大
                                    || (f1.source != null && f1.source.sign.equals(Constants.SIGN_SUBTRACT) && f1.source.f2.compareToByIndex(f2) > 0)
                                    // f2的来源不能为加减
                                    || (f2.source != null && (f2.source.sign.equals(Constants.SIGN_ADD) || f2.source.sign.equals(Constants.SIGN_SUBTRACT)))
                    )) tempList.add(new ArrayList<>(Arrays.asList(f1.subtract(f2))));
                    //乘
                    if (!(
                            // f1若没有来源或来源为加减，则f1顺序不能比f2大
                            ((f1.source == null || f1.source.sign.equals(Constants.SIGN_ADD) || f1.source.sign.equals(Constants.SIGN_SUBTRACT)) && f1.compareToByIndex(f2) > 0)
                                    // f1的来源若为乘，则后者的顺序不能比f2大
                                    || (f1.source != null && f1.source.sign.equals(Constants.SIGN_MULTIPLY) && f1.source.f2.compareToByIndex(f2) > 0)
                                    // f1的来源不能为除
                                    || (f1.source != null && f1.source.sign.equals(Constants.SIGN_DIVIDE))
                                    // f2的来源不能为乘除
                                    || (f2.source != null && (f2.source.sign.equals(Constants.SIGN_MULTIPLY) || f2.source.sign.equals(Constants.SIGN_DIVIDE)))
                    )) tempList.add(new ArrayList<>(Arrays.asList(f1.multiply(f2))));
                    //除
                    if (!(
                            // f1若等于f2，则f1的顺序不能小于f2
                            (f1.compareTo(f2) == 0 && f1.compareToByIndex(f2) < 0)
                                    // f2不能为0或1
                                    || (f2.compareTo(new MyFraction(0)) == 0 || f2.compareTo(new MyFraction(1, 1)) == 0)
                                    // f1的来源若为除，则f1来源的后者的顺序不能大于f2
                                    || (f1.source != null && f1.source.sign.equals(Constants.SIGN_DIVIDE) && f1.source.f2.compareToByIndex(f2) > 0)
                                    // f2的来源不能为乘除
                                    || (f2.source != null && (f2.source.sign.equals(Constants.SIGN_MULTIPLY) || f2.source.sign.equals(Constants.SIGN_DIVIDE)))
                    )) tempList.add(new ArrayList<>(Arrays.asList(f1.divide(f2))));

                    // 封装计算结果
                    tempList.forEach(temp -> {
                        if (!temp.isEmpty()) result.add(temp);
                    });

                    // 往list中放入没有进行运算的数
                    for (int k = 0; k < list.size(); k++)
                        if (k != i && k != j) for (List<MyFraction> temp : tempList) temp.add(list.get(k));
                }
            }
        }
        return result;
    }

}
